#pragma once
// #undef SPDLOG_ACTIVE_LEVEL
// #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_TRACE
#include "frontend.h"
#include "buffer.h"
#include "Cache/Cache.h"

class Backend : public CacheRecvInterface
{
public:
    uint64_t inst_cnt;
    uint64_t cycle;
    bool sim_finish;
    Frontend *frontend;
    Cache *cache;
    Backend() : inst_cnt(0),cycle(0) {};
    ~Backend() = default;
    virtual void run() = 0;
    virtual void reset() = 0;
};

class IdealBackend : public Backend
{
public:
    IdealBackend();
    ~IdealBackend();
    void run() override;
    void reset() override;
    bool recv(CacheOP *cop) override {return true;};
};

class OoOBackend : public Backend
{
public:
    typedef struct
    {
        bool valid;
    } PhysicalRegEntry;

    typedef struct
    {
        uint32_t preg_idx; /* physical reg index */
    } FreeListEntry;

    typedef struct
    {
        uint32_t preg_idx;
    } RenameTableEntry;

    typedef struct
    {
        FrontendOP fop;
        bool renamed;
        uint32_t prs[2];
        uint32_t pdst;
        uint32_t free_pdst;
    } RenamedOP;

    typedef struct
    {
        RenamedOP rop;
        int rob_index;
        CacheOP *cop;
    } BackendOP;
    ConstAllocator<BackendOP> bop_allocator;
    typedef struct
    {
        uint64_t cycle;
    } ForwardEntry;

    typedef struct
    {
        BackendOP *bop;
        bool finish;
        bool need_store;
        bool wait_store;
    } ROB_Entry;

    enum class FU_Type
    {
        ALU,
        MDU,
        BRU,
        LSU
    };

    struct
    {
        FU_Type type;
        int latnecy;
        const char *name;
    } fu_table[NUM_OP];

    ConstAllocator<CacheOP> cop_allocator;
    ConstList<FreeListEntry> freelist;
    std::vector<PhysicalRegEntry> prf;
    std::vector<RenameTableEntry> rat;
    ConstList<BackendOP *> rops;
    FIFO<ROB_Entry> robs;
    ConstList<BackendOP *> iq_alu, iq_mdu, iq_bru, iq_lsu;
    ConstList<std::tuple<BackendOP *, uint64_t>> ex_alu, ex_mdu, ex_bru, ex_lsu;

    ConstList<BackendOP *> ldq;
    FIFO<BackendOP *> stq;

    Counter frontend_stall, rob_full, prf_full, issue_stall;
    Counter iq_alu_full, iq_mdu_full, iq_bru_full, iq_lsu_full;
    Counter hazard_alu, hazard_mdu, hazard_bru, hazard_lsu;
    Counter commit_bound;
    Counter ldq_full, stq_full;
    Counter mem_hazard, ld2ld_hazard, ld2st_hazard, st2ld_hazard, st2st_hazard;
    Counter mem_byte_hazard, ld2ld_byte_hazard, ld2st_byte_hazard, st2ld_byte_hazard, st2st_byte_hazard;
    Counter st2ld_forward, total_mem_hazard;
    Counter rob_commit_stall_by_store;
    bool rop_ready(RenamedOP &rop);
    void execute_bru_op(RenamedOP &rop);
    void wakeup(RenamedOP &rop);
    void bop2cop(CacheOP *cop, BackendOP *bop);
    bool mem_hazard_check(CacheOP *cop);
    bool store_load_forward(CacheOP *cop);
    bool addr_hazard_check(word_t addr1, word_t size1, word_t addr2, word_t size2)
    {
        const word_t mask = ~((1ull << sizeof(word_t)) - 1ull);
        if ((addr1 & mask) != (addr2 & mask))
            return false;
        word_t mask1 = ((1 << size1) - 1) << (addr1 % 8);
        word_t mask2 = ((1 << size2) - 1) << (addr2 % 8);
        // printf("addr1:0x%lx addr2:0x%lx size1:%ld size2:%ld mask1:0x%lx mask2:0x%lx & 0x%lx\n", addr1, addr2, size1, size2, mask1, mask2, mask1 & mask2);
        if ((mask1 & mask2) == 0)
            return false;
        return true;
    }

    OoOBackend();
    ~OoOBackend();
    void run() override;
    void reset() override;

    void Rename();
    void Dispatch();
    void Issue();
    void Execute();
    void Commit();

    bool recv(CacheOP *cop) override;
};